package com.example.dwr.ticketcenter;

import com.example.dwr.people.RandomData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import jsx3.GI;
import jsx3.app.Cache;
import jsx3.app.Server;
import jsx3.gui.LayoutGrid;
import jsx3.gui.Matrix;
import jsx3.gui.Select;
import jsx3.gui.TextBox;
import jsx3.xml.CdfDocument;
import jsx3.xml.Record;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.Browser;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ScriptSessions;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.impl.DaemonThreadFactory;
import org.directwebremoting.ui.browser.Window;

public class CallCenter
  implements Runnable
{
  private static final String[] ELEMENTS = { "textPhone", "textName", "textAddress", "textPayment", "textNotes", "selectEvent", "selectPaymentType", "buttonBook", "buttonSupervisor", "buttonCancel" };
  private static int nextId = 1;
  private final List<Call> calls = Collections.synchronizedList(new ArrayList());
  private final Random random = new Random();
  private static final Log log = LogFactory.getLog(CallCenter.class);

  public CallCenter()
  {
    addRandomKnownCall();
    addRandomUnknownCall();
    addRandomUnknownCall();
    addRandomUnknownCall();

    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());

    executor.scheduleAtFixedRate(this, 2L, 2L, TimeUnit.SECONDS);
  }

  public void run()
  {
    try
    {
      synchronized (this.calls)
      {
        switch (this.random.nextInt(5))
        {
        case 0:
        case 1:
          addRandomUnknownCall();
          break;
        case 2:
          addRandomKnownCall();
          break;
        case 3:
          removeRandomCall();
        }

        updateAll();
      }
    }
    catch (Exception ex)
    {
      log.warn("Random event failure", ex);
    }
  }

  public void load()
  {
    deselect();
    update();
  }

  public void alertSupervisor(Call fromWeb)
  {
    ScriptSession session = WebContextFactory.get().getScriptSession();

    Object handlingId = session.getAttribute("handlingId");
    if (handlingId == null)
    {
      Window.alert("No call found");
      return;
    }

    synchronized (this.calls)
    {
      Call call = findCaller(handlingId);
      if (call == null){
	      Window.alert("That caller hung up, please select another");
	      return;
      }
     session.removeAttribute("handlingId");

      call.setName(fromWeb.getName());
      call.setAddress(fromWeb.getAddress());
      call.setNotes(fromWeb.getNotes());
      call.setHandlerId(null);
      call.setSupervisorAlert(true);

      deselect();

      updateAll();
    }
  }

  public void completeHandling(Call fromWeb)
  {
    ScriptSession session = WebContextFactory.get().getScriptSession();

    Object handlingId = session.getAttribute("handlingId");
    if (handlingId == null)
    {
      Window.alert("No call found");
      return;
    }

    synchronized (this.calls)
    {
      Call call = findCaller(handlingId);
      if (call != null){
      Window.alert("That caller hung up, please select another");
      return;
      }
      session.removeAttribute("handlingId");

      this.calls.remove(call);
      log.debug("Properly we should book a ticket for " + fromWeb.getPhoneNumber());

      deselect();
      updateAll();
    }
  }

  public void cancelHandling()
  {
    ScriptSession session = WebContextFactory.get().getScriptSession();

    Object handlingId = session.getAttribute("handlingId");
    if (handlingId == null)
    {
      Window.alert("That caller hung up, please select another");
      return;
    }

    synchronized (this.calls)
    {
      Call call = findCaller(handlingId);
      if (call == null){
	      log.debug("Cancel handling of call that hung up");
	      return;
      }
      session.removeAttribute("handlingId");
      call.setHandlerId(null);

      deselect();
      updateAll();
    }
  }

  public void beginHandling(String id)
  {
    ScriptSession session = WebContextFactory.get().getScriptSession();

    Object handlingId = session.getAttribute("handlingId");
    if (handlingId != null)
    {
      Window.alert("Please finish handling the current call before selecting another");
      return;
    }

    synchronized (this.calls)
    {
      Call call = findCaller(id);
      if (call == null)
      {
        log.debug("Caller not found: " + id);
        Window.alert("That caller hung up, please select another"); 
        return;
      }

      if (call.getHandlerId() == null){
	      Window.alert("That call is being handled, please select another");
	      return;
      }

       session.setAttribute("handlingId", id);
      call.setHandlerId(session.getId());

      select(call);
      updateAll();
    }
  }

  private Call findCaller(Object attribute)
  {
    int id;
    try
    {
      id = Integer.parseInt(attribute.toString());

      for (Iterator i$ = this.calls.iterator(); i$.hasNext(); ) { Call call = (Call)i$.next();

        if (call.getId() == id)
        {
          return call;
        }
      }

      return null;
    }
    catch (NumberFormatException ex)
    {
      log.warn("Illegal number format: " + attribute.toString(), ex); }
    return null;
  }

  protected void removeRandomCall()
  {
    if (!(this.calls.isEmpty()))
    {
      synchronized (this.calls)
      {
        int toDelete = this.random.nextInt(this.calls.size());
        Call removed = (Call)this.calls.remove(toDelete);

        String sessionId = removed.getHandlerId();
        if (sessionId != null)
        {
          Browser.withSession(sessionId, new Runnable()
          {
            public void run()
            {
              ScriptSessions.removeAttribute("handlingId");
              Window.alert("It appears that this caller has hung up. Please select another.");
            }
          });
        }

        updateAll();
      }
    }
  }

  protected void addRandomKnownCall()
  {
    if (this.calls.size() < 10)
    {
      Call call = new Call();
      call.setId(getNextId());
      call.setName(RandomData.getFullName());
      String[] addressAndNumber = RandomData.getAddressAndNumber();
      call.setAddress(addressAndNumber[0]);
      call.setPhoneNumber(addressAndNumber[1]);

      this.calls.add(call);
    }
  }

  protected void addRandomUnknownCall()
  {
    if (this.calls.size() < 10)
    {
      String phoneNumber = RandomData.getPhoneNumber(this.random.nextInt(3) != 0);
      Call call = new Call();
      call.setPhoneNumber(phoneNumber);
      call.setId(getNextId());
      this.calls.add(call);
    }
  }

  protected void updateAll()
  {
    String contextPath = ServerContextFactory.get().getContextPath();
    if (contextPath == null)
    {
      return;
    }

    String page = contextPath + "/gi/ticketcenter.html";
    Browser.withPage(page, new Runnable()
    {
      public void run()
      {
        update();
      }
    });
  }

  protected void update()
  {
    CdfDocument cdfdoc = new CdfDocument("jsxroot");
    for (Iterator i$ = this.calls.iterator(); i$.hasNext(); ) { Call call = (Call)i$.next();

      cdfdoc.appendRecord(new Record(call));
    }

    Server tc = GI.getServer("ticketcenter");
    tc.getCache().setDocument("callers", cdfdoc);
    ((Matrix)tc.getJSXByName("listCallers", Matrix.class)).repaint(null);
  }

  private void select(Call call)
  {
    Server ticketcenter = GI.getServer("ticketcenter");

    ((TextBox)ticketcenter.getJSXByName("textPhone", TextBox.class)).setValue(call.getPhoneNumber());
    ((TextBox)ticketcenter.getJSXByName("textName", TextBox.class)).setValue(call.getName());
    ((TextBox)ticketcenter.getJSXByName("textNotes", TextBox.class)).setValue(call.getNotes());
    ((Select)ticketcenter.getJSXByName("selectEvent", Select.class)).setValue(null);

    setFormEnabled(true);
  }

  private void deselect()
  {
    Server ticketcenter = GI.getServer("ticketcenter");

    ((TextBox)ticketcenter.getJSXByName("textPhone", TextBox.class)).setValue("");
    ((TextBox)ticketcenter.getJSXByName("textName", TextBox.class)).setValue("");
    ((TextBox)ticketcenter.getJSXByName("textNotes", TextBox.class)).setValue("");
    ((Select)ticketcenter.getJSXByName("selectEvent", Select.class)).setValue(null);

    setFormEnabled(false);
  }

  private void setFormEnabled(boolean enabled)
  {
    int state = (enabled) ? 1 : 0;

    Server ticketcenter = GI.getServer("ticketcenter");
    String[] arr$ = ELEMENTS; int len$ = arr$.length; for (int i$ = 0; i$ < len$; ++i$) { String element = arr$[i$];

      ((TextBox)ticketcenter.getJSXByName(element, TextBox.class)).setEnabled(state, true);
    }

    LayoutGrid layoutForm = (LayoutGrid)ticketcenter.getJSXByName("layoutForm", LayoutGrid.class);
    layoutForm.setBackgroundColor((enabled) ? "#FFF" : "#EEE", true);
  }

  public static synchronized int getNextId()
  {
    return (nextId++);
  }
}